package cn.iocoder.yudao.framework.datasource.core;

import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 动态路由数据源
 * 继承 AbstractRoutingDataSource，实现动态数据源路由逻辑
 * 支持数据源动态创建、检查、删除，集成Druid连接池管理
 *
 * @author 芋道源码
 */
@Slf4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    /**
     * 数据源映射，用于存储动态创建的数据源
     */
    private final Map<Object, Object> dynamicDataSources = new ConcurrentHashMap<>();

    /**
     * 默认数据源标识
     */
    private static final String DEFAULT_DATASOURCE = "master";

    @Override
    protected Object determineCurrentLookupKey() {
        String dataSourceKey = DataSourceContextHolder.getDataSource();
        log.debug("当前数据源标识: {}", dataSourceKey);
        return StrUtil.isNotBlank(dataSourceKey) ? dataSourceKey : DEFAULT_DATASOURCE;
    }

    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        super.setTargetDataSources(targetDataSources);
        this.dynamicDataSources.putAll(targetDataSources);
    }

    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        log.info("DynamicRoutingDataSource 初始化完成，默认数据源: {}", DEFAULT_DATASOURCE);
    }

    /**
     * 检查并创建数据源
     * 如果数据源不存在或连接失效，则创建新的数据源连接
     *
     * @param dataSourceConfig 数据源配置
     */
    public void checkCreateDataSource(DataSourceConfigDO dataSourceConfig) {
        String dataSourceKey = dataSourceConfig.getDataSourceKey();
        
        // 检查数据源是否已存在
        if (dynamicDataSources.containsKey(dataSourceKey)) {
            DataSource existingDataSource = (DataSource) dynamicDataSources.get(dataSourceKey);
            if (isConnectionValid(existingDataSource)) {
                log.debug("数据源 {} 已存在且连接有效", dataSourceKey);
                return;
            } else {
                log.warn("数据源 {} 连接失效，重新创建", dataSourceKey);
                delDatasource(dataSourceKey);
            }
        }

        // 创建新数据源
        createDataSource(dataSourceConfig);
    }

    /**
     * 创建新数据源
     *
     * @param dataSourceConfig 数据源配置
     */
    private void createDataSource(DataSourceConfigDO dataSourceConfig) {
        try {
            DruidDataSource dataSource = new DruidDataSource();
            
            // 基本连接信息
            dataSource.setUrl(dataSourceConfig.getUrl());
            dataSource.setUsername(dataSourceConfig.getUsername());
            dataSource.setPassword(dataSourceConfig.getPassword());
            dataSource.setDriverClassName(getDriverClassName(dataSourceConfig.getType()));
            
            // 连接池配置
            dataSource.setInitialSize(5);
            dataSource.setMinIdle(10);
            dataSource.setMaxActive(20);
            dataSource.setMaxWait(60000);
            dataSource.setTimeBetweenEvictionRunsMillis(60000);
            dataSource.setMinEvictableIdleTimeMillis(600000);
            dataSource.setMaxEvictableIdleTimeMillis(1800000);
            dataSource.setValidationQuery("SELECT 1 FROM DUAL");
            dataSource.setTestWhileIdle(true);
            dataSource.setTestOnBorrow(false);
            dataSource.setTestOnReturn(false);
            dataSource.setPoolPreparedStatements(true);
            dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
            
            // 初始化数据源
            dataSource.init();
            
            // 测试连接
            try (Connection connection = dataSource.getConnection()) {
                log.info("数据源 {} 连接测试成功", dataSourceConfig.getDataSourceKey());
            }
            
            // 添加到动态数据源映射中
            dynamicDataSources.put(dataSourceConfig.getDataSourceKey(), dataSource);
            
            // 更新父类的目标数据源
            setTargetDataSources(dynamicDataSources);
            afterPropertiesSet();
            
            log.info("成功创建数据源: {}", dataSourceConfig.getDataSourceKey());
            
        } catch (Exception e) {
            log.error("创建数据源失败: {}", dataSourceConfig.getDataSourceKey(), e);
            throw new RuntimeException("创建数据源失败: " + e.getMessage(), e);
        }
    }

    /**
     * 删除数据源
     *
     * @param dataSourceKey 数据源标识
     */
    private void delDatasource(String dataSourceKey) {
        try {
            Object dataSource = dynamicDataSources.get(dataSourceKey);
            if (dataSource instanceof DruidDataSource) {
                ((DruidDataSource) dataSource).close();
                log.info("关闭数据源连接池: {}", dataSourceKey);
            }
            
            dynamicDataSources.remove(dataSourceKey);
            setTargetDataSources(dynamicDataSources);
            afterPropertiesSet();
            
            log.info("成功删除数据源: {}", dataSourceKey);
            
        } catch (Exception e) {
            log.error("删除数据源失败: {}", dataSourceKey, e);
        }
    }

    /**
     * 检查数据源连接是否有效
     *
     * @param dataSource 数据源
     * @return 连接是否有效
     */
    private boolean isConnectionValid(DataSource dataSource) {
        try (Connection connection = dataSource.getConnection()) {
            return connection != null && !connection.isClosed();
        } catch (SQLException e) {
            log.warn("数据源连接检查失败", e);
            return false;
        }
    }

    /**
     * 根据数据库类型获取驱动类名
     *
     * @param type 数据库类型
     * @return 驱动类名
     */
    private String getDriverClassName(String type) {
        return DataSourceTypeEnum.getDriverClassName(type);
    }

    /**
     * 获取当前所有数据源
     *
     * @return 数据源映射
     */
    public Map<Object, Object> getDynamicDataSources() {
        return dynamicDataSources;
    }
}